Release 10.1A: OpenEdge Development:
Progress 4GL Handbook
Writing the BinCheck procedure to check inventory
In this section, you add another procedure call that illustrates some of the block types you studied in this chapter and some of the list handling functions summarized in Chapter 1, " Introducing the Progress 4GL." The procedure looks at the Warehouse and Bin tables to see which Warehouses can and cannot supply the Items for a given Order.
![]()
To modify the
h-OrderCalcs.pprocedure, add the new statements in bold type:
The additional code defines two new
OUTPUTparameters and a new variable.Then, as part of the
ASSIGNstatement, it constructs a list of Item numbers for the OrderLines of the Order. To make a list, it uses aCHARACTERvariablecItemList. The assignment statement effectively means:
- Take the current value of the
cItemListvariable (which is initially blank).- If it’s blank, then append a blank value to it (this is just a no-op condition for the
IF-THEN-ELSEstatement, which requires both aTHENphrase and anELSEphrase). Otherwise, if there’s already something in the list, append a comma to it to separate the Items.- Use the
STRINGbuilt-in function to convert the integerItemNumto aCHARACTERvalue and append it to the variable. (There are other built-in functions like this one namedDECIMAL,INTEGER,DATE, andLOGICALto convert character strings to those other data types as well.)At the end of the
FOR EACHblock,cItemNumholds a comma-separated list of all the Items for the current Order.Finally, the procedure runs another procedure,
h-BinCheck.p, which you’ll write next.
![]()
To write the
h-BinCheck.pprocedure:
- Save this modified version of
h-OrderCalcs.p.- Open a New Procedure Window and start to write
h-BinCheck.pwith the following code. Add each new group of statements to the procedure as they are discussed:
This procedure takes the list of item numbers as an
INPUTparameter and returns twoCHARACTERparameters. The various variables are used throughout the procedure. Remember that you can use the editor shortcuts (IPC, OPC, DVI, and DVC) to generate most of theDEFINEPARAMETERandVARIABLEstatements for you.Using list and string functions to manage a list of values
The
h-BinCheck.pprocedure needs to make a list of how many Items are supplied by each Warehouse. There are various ways to code this, but to illustrate some more of the string manipulation functions you were introduced to in Chapter 2, "Using Basic 4GL Constructs," you’ll build this as a character string.
![]()
To update
h-BinCheck.pto make a list of the number of Items supplied by each Warehouse:
- Add placeholders for the count of Items in each Warehouse. The following code forms a list with as many zeroes as there are Warehouses. The zero values are later incremented to count Items supplied by each Warehouse:
Note: The
RIGHT-TRIMfunction removes the final comma from the list, rather than theIF-THEN-ELSEstatement in the assignment that created the item list inOrderProcs.p. These are just different ways of doing the same thing. TheRIGHT-TRIMfunction is a bit more efficient.- To loop through the list of Items, add a
DOblock with theNUM-ENTRIESfunction:
NUM-ENTRIEScounts the entries in a list using a comma as the delimiter between entries by default. If you need to use a delimiter other than a comma, the delimiter can be an optional second argument to the function.- Add a statement that embeds two built-in functions into one statement:
The
ENTRYfunction extracts entry numberiEntryfrompcItemList. It returns this to theINTEGERfunction, which converts the value back to an integer. So now you’ve restored the Item number to its original form.- Add a block of code that operates on this Item number. The Bin table represents bins or containers in each Warehouse that are used to store the various Items. It has both an ItemNum field to point to the Item record, and a WarehouseNum field to point to the Warehouse where the Bin is located. If the Qty (quantity) field for a Bin record is 0, then the Warehouse that Bin is in cannot supply that part. The code builds up this list of Warehouse names. The
LOOKUPfunction looks for a string in a list. If it finds it, it returns the position of the entry in the list. Otherwise, it returns 0 if the entry is not in the list. Here theLOOKUPfunction is used to make sure that a Warehouse name is added to the list once only if it’s not already there:
- Still within the
DOblock that iterates on each item, add code that initializes two variables to zero using a singleASSIGNstatement:
These variables hold the quantity of each item at a Warehouse and the Warehouse number.
![]()
To use the
REPEAT PRESELECTblock to pre-fetch records:
- Add a
REPEATblock that preselects each Bin that holds the current Item, along with the Warehouse where the Bin is located, filtering these to include only Warehouses in the USA. The records are sorted in descending order of their quantity. This identifies which Warehouse has the largest quantity of the Item in inventory. Remember that thePRESELECTphrase forces Progress to retrieve all the matching records before beginning to execute the statements in the block:
- Add the code that finds the next Warehouse record in this preselected list. The first time through the
REPEATblock, theFIND NEXTstatement finds the first record:
Why does the statement name the Warehouse buffer and not the Bin? The rule is that whenever you are doing a
FINDon aPRESELECTresult set that involves a join, you must name the last table in the join. This makes sense, because if it is a one-to-many join, the record in the last (rightmost) table in the join is the only one to change on every iteration. The first table in the join might be the same for a number of records in the second table.Remember also that the
REPEATblock does not automatically iterate for you, even if you preselect the records. You have to use aFINDstatement to move from record to record.- Add the following statements to determine whether the Warehouse with the highest inventory for the Item has a quantity at least 100 greater than the next best Warehouse. If so, it retrieves the entry in the list of best Warehouses that the code initialized with zeroes at the start of the procedure, increments it, and puts it back in the list, doing the necessary conversions to and from the
INTEGERdata type:
- Terminate the
REPEATblock and theDOblock for each item:
Using multiple weak-scoped references in a single block
If you take a look at the entire
DOblock, you can inspect the buffer scoping:
The
DOblock itself doesn’t scope any records. TheFOR EACHblock and theREPEAT PRESELECT EACHblock each scope the Bin record with a weak scope. This is okay, and the Bin buffer is scoped to each of these two blocks in turn.The final block of code walks through the list of best Warehouses for this Order’s items. At this point the
cBestListvariable holds a list of numbers for each Warehouse. Each number is the count of Items where that Warehouse has an inventory at least 100 better than the next best Warehouse. This block checks whether there’s a Warehouse that is the best for either all or all but one of the Items. If so, you find that Warehouse record and save off the WarehouseName to pass back. By now all the statements and functions in this block should be familiar to you.
![]()
To end the procedure, use the following code:
This procedure is a little complicated, but these examples show how the different block types interact and how to use some of the built-in functions listed in Chapter 2, " Using Basic 4GL Constructs."
Examining the scope with weak and strong references
One final question before you move on: The Bin record buffer is scoped to the two blocks inside the main
DOblock, but at what level is the Warehouse record buffer scoped? Look back through the entire procedure to come up with an answer before looking at this excerpt from the listing file:
You see the two blocks where the Bin buffer is scoped. You also see that the Warehouse buffer is scoped to the entire procedure (line 0). Why is this?
There are several free references to the Warehouse buffer that aren’t in blocks that provide record scoping. This includes, among others, the final
DOblock of the procedure. As a result, Progress raises the scope of the buffer all the way to the procedure itself because there’s no other block to scope it to. In your sample procedure, which is only reading records from the database and not updating them, it doesn’t make a lot of difference. If the procedure had a transaction that updated the Warehouse record, though, you might find that the record and the record lock on it are held much longer than you expected or wanted, resulting in record contention between different users accessing the table at the same time.What could you do to avoid this? Define a strong scope for the Warehouse record wherever it’s used.
![]()
To define a strong scope for the Warehouse record:
- First make the first
DOblock around theFIND Warehousestatement scope the buffer to the block:
- Press SHIFT+F2 to do a syntax check. What do you get?
![]()
Why did this error happen? You tried to force the scope of the buffer to this block, but the free reference in the
DOblock at the end of the procedure still forces the scope up to the top, and those two conflict.- Change the final
DOblock to place a strong scope there:
Now a syntax check succeeds. If you compile the procedure and get a listing file, you see that the Warehouse buffer is scoped all over the place:
Take a look at the scope for each of these blocks:
- The Warehouse buffer is now scoped to the very first
FOR EACH Warehouseblock, with its weak scope. Progress tried to do this before. Because the other references to Warehouse forced the scope up to the procedure level, this weak scope disappeared (that’s why it’s called weak).- It’s scoped to the
DO FOR Warehouseblock because you added that strong-scoped reference.- It’s also scoped to the
REPEAT PRESELECTblock. This is another weak scope that didn’t hold when the buffer scope was forced to the top.- It’s scoped to the final
DO FOR Warehouseblock, with its strong-scoped reference you just added.When you start writing serious procedures that update the database, you’ll be a lot more successful if you keep your buffer scope small like this. You should get into the habit now.
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |